//
//  Canvas.m
//  Shake and Bake
//
//  Created by Antoine Quint on 26/06/07.
//  Copyright 2007 Fuchsia Design. All rights reserved.
//

#import "Canvas.h"
#import "SVGRect.h"
#import "SVGCircle.h"
#import "SVGEllipse.h"
#import "SVGGroup.h"
#import "SVGPath.h"
#import "SVGTextElement.h"
#import "SVGAnimateElement.h"
#import "SVGAnimateTransformElement.h"
#import "SVGVideoElement.h"
#import "SVGImageElement.h"

@implementation Canvas

- (void)awakeFromNib
{
	NSLog(@"Time to shake and bake!");
}

/***** INIT STUFF *****/

- (id)initWithFrame:(NSRect)frame
{
  [super initWithFrame:frame];
	if (self) {
    //[self registerForDraggedTypes:[NSArray arrayWithObjects:@"xml", @"svg", nil]];
    [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
	}
	return self;
}

- (void)dealloc
{
  [self unregisterDraggedTypes];
  [super dealloc];
}

- (BOOL)isOpaque
{
  return NO;
}

/***** DRAG AND DROP SUPPORT *****/

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
  if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric) {
    //this means that the sender is offering the type of operation we want
    //return that we want the NSDragOperationGeneric operation that they 
    //are offering
    return NSDragOperationGeneric;
  }
  else {
    //since they aren't offering the type of operation we want, we have 
    //to tell them we aren't interested
    return NSDragOperationNone;
  }
}

- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
{
  return YES;
}

- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
  NSPasteboard *paste = [sender draggingPasteboard];
  [self loadFile:[[paste propertyListForType:@"NSFilenamesPboardType"] objectAtIndex:0]];
  return YES;
}

/***** FILE LOADING *****/

- (void)loadFile:(NSString *)pathToFile
{
	// parse the file
	if (parser) {
		[parser release]; 
	}	
	parser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL fileURLWithPath:pathToFile]];
	[parser setDelegate:self]; 
	[parser setShouldResolveExternalEntities:YES];
	[parser setShouldProcessNamespaces:YES];
  [parser setShouldReportNamespacePrefixes:YES];
	BOOL success = [parser parse];
	NSLog(@"Parsed file %@ with success %d", pathToFile, success);	
}

/***** NSXMLParser INTERFACE *****/

- (void)parserDidStartDocument:(NSXMLParser *)parser
{
	// setup JS document object
  jsContext = JSGlobalContextCreate(NULL);
  jsGlobal = JSContextGetGlobalObject(jsContext);

  /*
  JSStringRef document = JSStringCreateWithUTF8CString("Document");
  JSObjectSetProperty(jsContext, jsGlobal, document, JSObjectMakeConstructor(jsContext, JSDocument_class(jsContext), JSDocument_construct), kJSPropertyAttributeNone, NULL);
  JSStringRelease(document);
  */

	// setup containers array where we'll keep track
	// of opened containers as we parse the tree
	elementsStack = [NSMutableArray array];
  namespaces = [NSMutableDictionary dictionary];
  ids = [NSMutableDictionary dictionary];
	// setup the root layer
	NSSize size = [self bounds].size;
	rootLayer = [CALayer layer];
	rootLayer.position = CGPointMake(0.0f, 0.0f);
	rootLayer.bounds = CGRectMake(0.0f, 0.0f, size.width, size.height);
	[self setWantsLayer:YES];
	[self setLayer:rootLayer];
	// change the coordinate system to y-down
	CATransform3D flipper = {
		1.0f, 0.0f, 0.0f, 0.0f,
		0.0f, -1.0f, 0.0f, 0.0f,
		0.0f, 0.0f, 1.0f, 0.0f,
		0.0f, 0.0f, 0.0f, 1.0f
	};
	rootLayer.sublayerTransform = flipper;
	// finally, add the element to the stack
	[elementsStack addObject:rootLayer];
}

- (void)parserDidEndDocument:(NSXMLParser *)parser
{
  //NSLog(@"have found %d items with IDs set", [ids count]);
}

- (void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName 
	 namespaceURI:(NSString *)namespaceURI
	qualifiedName:(NSString *)qualifiedName 
     attributes:(NSDictionary *)attributeDict
{
  
  
  // use some of that to get archival of state working
  // CGContextRestoreGState()
  // CGContextSaveGState()
  
  //NSLog(@"started parsing element: %@", elementName);
    
	// check if it's an SVG element, if so, process it
	if ([namespaceURI isEqualToString:NS_URI_SVG]) {
		CALayer *element;
		if ([elementName isEqualToString:ELEMENT_NAME_G]) {
			element = [[SVGGroup alloc] initWithAttributeDict:attributeDict andView:self];
		}
		else if ([elementName isEqualToString:ELEMENT_NAME_RECT]) {
			element = [[SVGRect alloc] initWithAttributeDict:attributeDict andView:self];
		}
		else if ([elementName isEqualToString:ELEMENT_NAME_CIRCLE]) {
			element = [[SVGCircle alloc] initWithAttributeDict:attributeDict andView:self];
		}
		else if ([elementName isEqualToString:ELEMENT_NAME_ELLIPSE]) {
			element = [[SVGEllipse alloc] initWithAttributeDict:attributeDict andView:self];
		}
		else if ([elementName isEqualToString:ELEMENT_NAME_PATH]) {
			element = [[SVGPath alloc] initWithAttributeDict:attributeDict andView:self];
		}
		else if ([elementName isEqualToString:ELEMENT_NAME_TEXT]) {
			element = [[SVGTextElement alloc] initWithAttributeDict:attributeDict andView:self];
		}
		else if ([elementName isEqualToString:ELEMENT_NAME_VIDEO]) {
			element = [[SVGVideoElement alloc] initWithAttributeDict:attributeDict andView:self];
		}
		else if ([elementName isEqualToString:ELEMENT_NAME_IMAGE]) {
			element = [[SVGImageElement alloc] initWithAttributeDict:attributeDict andView:self];
		}
		else if ([elementName isEqualToString:ELEMENT_NAME_ANIMATE]) {
			// XXX should track we're adding this attribute
      [[SVGAnimateElement alloc] initWithAttributeDict:attributeDict andParent:[self currentElement]];
			return;
		}
		else if ([elementName isEqualToString:ELEMENT_NAME_ANIMATE_TRANSFORM]) {
			// XXX should track we're adding this attribute
      [[SVGAnimateTransformElement alloc] initWithAttributeDict:attributeDict andParent:[self currentElement]];
			return;
		}
		else {
			// should find a better way to check we actually encountered
			// an object we can take care of
			return;
		}
		// add the layer to the current layer
		[[self currentElement] addSublayer:element];
		// add the new element to the elements stack
		[elementsStack addObject:element];
	}
}

- (void)parser:(NSXMLParser *)parser
 didEndElement:(NSString *)elementName
	namespaceURI:(NSString *)namespaceURI
 qualifiedName:(NSString *)qName
{
  //NSLog(@"finished parsing element: %@", elementName);
	if ([namespaceURI isEqualToString:NS_URI_SVG] &&
	    ![elementName isEqualToString:ELEMENT_NAME_ANIMATE_TRANSFORM] &&
	    ![elementName isEqualToString:ELEMENT_NAME_ANIMATE]) {
		[elementsStack removeLastObject];
	}
}

- (void) parser:(NSXMLParser *)parser 
foundCharacters:(NSString *)string
{
  SVGElement *currentOpenElement = [self currentElement];
  if ([currentOpenElement isKindOfClass:[SVGTextElement class]]) {
    [(SVGTextElement *)currentOpenElement addToTextContent:string];
  }
}

- (void)       parser:(NSXMLParser *)parser
didStartMappingPrefix:(NSString *)prefix
                toURI:(NSString *)namespaceURI
{
  [namespaces setValue:prefix forKey:namespaceURI];
}

- (NSString *)getQNameForAttribute:(NSString *)localName inNamespace:(NSString *)namespaceURI
{
  return [NSString stringWithFormat:@"%@:href", [namespaces valueForKey:namespaceURI]];		
}

- (SVGElement *)currentElement
{
	return [elementsStack lastObject];
}

- (NSMutableDictionary *)ids
{
  return ids;
}

@end
